home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / NetHack 3.1.3 / source / src / mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-01  |  14.6 KB  |  590 lines  |  [TEXT/R*ch]

  1. /*    SCCS Id: @(#)mail.c    3.1    93/05/15    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. #include "hack.h"
  6.  
  7. #ifdef MAIL
  8. #include "mail.h"
  9. /*
  10.  * Notify user when new mail has arrived.  Idea by Merlyn Leroy.
  11.  *
  12.  * The mail daemon can move with less than usual restraint.  It can:
  13.  *    - move diagonally from a door
  14.  *    - use secret and closed doors
  15.  *    - run through a monster ("Gangway!", etc.)
  16.  *    - run over pools & traps
  17.  *
  18.  * Possible extensions:
  19.  *    - Open the file MAIL and do fstat instead of stat for efficiency.
  20.  *      (But sh uses stat, so this cannot be too bad.)
  21.  *    - Examine the mail and produce a scroll of mail named "From somebody".
  22.  *    - Invoke MAILREADER in such a way that only this single letter is read.
  23.  *    - Do something to the text when the scroll is enchanted or cancelled.
  24.  *    - Make the daemon always appear at a stairwell, and have it find a
  25.  *      path to the hero.
  26.  *
  27.  * Note by Olaf Seibert: On the Amiga, we usually don't get mail.  So we go
  28.  *             through most of the effects at 'random' moments.
  29.  */
  30.  
  31. static boolean FDECL(md_start,(coord *));
  32. static boolean FDECL(md_stop,(coord *, coord *));
  33. static boolean FDECL(md_rush,(struct monst *,int,int));
  34. static void FDECL(newmail, (struct mail_info *));
  35.  
  36. extern char *viz_rmin, *viz_rmax;    /* line-of-sight limits (vision.c) */
  37.  
  38. #ifdef OVL0
  39.  
  40. # if !defined(UNIX) && !defined(VMS)
  41. int mustgetmail = -1;
  42. # endif
  43.  
  44. #endif /* OVL0 */
  45. #ifdef OVLB
  46.  
  47. # ifdef UNIX
  48. #  include <sys/stat.h>
  49. #  include <pwd.h>
  50. /* DON'T trust all Unices to declare getpwuid() in <pwd.h> */
  51. #  if !defined(_BULL_SOURCE) && !defined(sgi) && !defined(_M_UNIX)
  52. /* DO trust all SVR4 to typedef uid_t in <sys/types.h> (probably to a long) */
  53. #   if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
  54. extern struct passwd *FDECL(getpwuid,(uid_t));
  55. #   else 
  56. extern struct passwd *FDECL(getpwuid,(int));
  57. #   endif
  58. #  endif
  59. static struct stat omstat,nmstat;
  60. static char *mailbox = NULL;
  61. static long laststattime;
  62.  
  63. # ifdef AMS                /* Just a placeholder for AMS */
  64. #   define MAILPATH "/dev/null"
  65. # else
  66. #  if defined(BSD) || defined(ULTRIX)
  67. #   define MAILPATH "/usr/spool/mail/"
  68. #  endif
  69. #  if defined(SYSV) || defined(HPUX)
  70. #   define MAILPATH "/usr/mail/"
  71. #  endif
  72. # endif /* AMS */
  73.  
  74. void
  75. getmailstatus()
  76. {
  77.     if(!mailbox && !(mailbox = getenv("MAIL"))) {
  78. #  ifdef MAILPATH
  79. #   ifdef AMS
  80.             struct passwd ppasswd;
  81.  
  82.         (void) memcpy(&ppasswd, getpwuid(getuid()), sizeof(struct passwd));
  83.         if (ppasswd.pw_dir) {
  84.              mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)+sizeof(AMS_MAILBOX));
  85.              Strcpy(mailbox, ppasswd.pw_dir);
  86.              Strcat(mailbox, AMS_MAILBOX);
  87.         } else
  88.           return;
  89. #   else
  90.         mailbox = (char *) alloc(sizeof(MAILPATH)+8);
  91.         Strcpy(mailbox, MAILPATH);
  92.         Strcat(mailbox, getpwuid(getuid())->pw_name);
  93. #  endif /* AMS */
  94. #  else
  95.         return;
  96. #  endif
  97.     }
  98.     if(stat(mailbox, &omstat)){
  99. #  ifdef PERMANENT_MAILBOX
  100.         pline("Cannot get status of MAIL=\"%s\".", mailbox);
  101.         mailbox = 0;
  102. #  else
  103.         omstat.st_mtime = 0;
  104. #  endif
  105.     }
  106. }
  107. # endif /* UNIX */
  108.  
  109. /*
  110.  * Pick coordinates for a starting position for the mail daemon.  Called
  111.  * from newmail() and newphone().
  112.  */
  113. static boolean
  114. md_start(startp)
  115.     coord *startp;
  116. {
  117.     coord testcc;    /* scratch coordinates */
  118.     int row;        /* current row we are checking */
  119.     int lax;        /* if TRUE, pick a position in sight. */
  120.     int dd;        /* distance to current point */
  121.     int max_distance;    /* max distance found so far */
  122.  
  123.     /*
  124.      * If blind and not telepathic, then it doesn't matter what we pick ---
  125.      * the hero is not going to see it anyway.  So pick a nearby position.
  126.      */
  127.     if (Blind && !Telepat) {
  128.     if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0))
  129.         return FALSE;    /* no good posiitons */
  130.     return TRUE;
  131.     }
  132.  
  133.     /*
  134.      * Arrive at an up or down stairwell if it is in line of sight from the
  135.      * hero.
  136.      */
  137.     if (couldsee(upstair.sx, upstair.sy)) {
  138.     startp->x = upstair.sx;
  139.     startp->y = upstair.sy;
  140.     return TRUE;
  141.     }
  142.     if (couldsee(dnstair.sx, dnstair.sy)) {
  143.     startp->x = dnstair.sx;
  144.     startp->y = dnstair.sy;
  145.     return TRUE;
  146.     }
  147.  
  148.     /*
  149.      * Try to pick a location out of sight next to the farthest position away
  150.      * from the hero.  If this fails, try again, just picking the farthest
  151.      * position that could be seen.  What we really ought to be doing is
  152.      * finding a path from a stairwell...
  153.      *
  154.      * The arrays viz_rmin[] and viz_rmax[] are set even when blind.  These
  155.      * are the LOS limits for each row.
  156.      */
  157.     lax = 0;    /* be picky */
  158.     max_distance = -1;
  159. retry:
  160.     for (row = 0; row < ROWNO; row++) {
  161.     if (viz_rmin[row] < viz_rmax[row]) {
  162.         /* There are valid positions on this row. */
  163.         dd = distu(viz_rmin[row],row);
  164.         if (dd > max_distance) {
  165.         if (lax) {
  166.             max_distance = dd;
  167.             startp->y = row;
  168.             startp->x = viz_rmin[row];
  169.             
  170.         } else if (enexto(&testcc, (xchar)viz_rmin[row], row,
  171.                         (struct permonst *) 0) &&
  172.                !cansee(testcc.x, testcc.y) &&
  173.                couldsee(testcc.x, testcc.y)) {
  174.             max_distance = dd;
  175.             *startp = testcc;
  176.         }
  177.         }
  178.         dd = distu(viz_rmax[row],row);
  179.         if (dd > max_distance) {
  180.         if (lax) {
  181.             max_distance = dd;
  182.             startp->y = row;
  183.             startp->x = viz_rmax[row];
  184.             
  185.         } else if (enexto(&testcc, (xchar)viz_rmax[row], row,
  186.                         (struct permonst *) 0) &&
  187.                !cansee(testcc.x,testcc.y) &&
  188.                couldsee(testcc.x, testcc.y)) {
  189.  
  190.             max_distance = dd;
  191.             *startp = testcc;
  192.         }
  193.         }
  194.     }
  195.     }
  196.  
  197.     if (max_distance < 0) {
  198.     if (!lax) {
  199.         lax = 1;        /* just find a position */
  200.         goto retry;
  201.     }
  202.     return FALSE;
  203.     }
  204.  
  205.     return TRUE;
  206. }
  207.  
  208. /*
  209.  * Try to choose a stopping point as near as possible to the starting
  210.  * position while still adjacent to the hero.  If all else fails, try
  211.  * enexto().  Use enexto() as a last resort because enexto() chooses
  212.  * its point randomly, which is not what we want.
  213.  */
  214. static boolean
  215. md_stop(stopp, startp)
  216.     coord *stopp;    /* stopping position (we fill it in) */
  217.     coord *startp;    /* starting positon (read only) */
  218. {
  219.     int x, y, distance, min_distance = -1;
  220.  
  221.     for (x = u.ux-1; x <= u.ux+1; x++)
  222.     for (y = u.uy-1; y <= u.uy+1; y++) {
  223.         if (!isok(x, y) || (x == u.ux && y == u.uy)) continue;
  224.  
  225.         if (ACCESSIBLE(levl[x][y].typ) && !MON_AT(x,y)) {
  226.         distance = dist2(x,y,startp->x,startp->y);
  227.         if (min_distance < 0 || distance < min_distance ||
  228.             (distance == min_distance && rn2(2))) {
  229.             stopp->x = x;
  230.             stopp->y = y;
  231.             min_distance = distance;
  232.         }
  233.         }
  234.     }
  235.  
  236.     /* If we didn't find a good spot, try enexto(). */
  237.     if (min_distance < 0 &&
  238.         !enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON]))
  239.     return FALSE;
  240.  
  241.     return TRUE;
  242. }
  243.  
  244. /* Let the mail daemon have a larger vocabulary. */
  245. static NEARDATA const char *mail_text[] = {
  246.     "Gangway!",
  247.     "Look out!",
  248.     "Pardon me!"
  249. };
  250. #define md_exclamations()    (mail_text[rn2(3)])
  251.  
  252. /*
  253.  * Make the mail daemon run through the dungeon.  The daemon will run over
  254.  * any monsters that are in its path, but will replace them later.  Return
  255.  * FALSE if the md gets stuck in a position where there is a monster.  Return
  256.  * TRUE otherwise.
  257.  */
  258. static boolean
  259. md_rush(md,tx,ty)
  260.     struct monst *md;
  261.     register int tx, ty;        /* destination of mail daemon */
  262. {
  263.     struct monst *mon;            /* displaced monster */
  264.     register int dx, dy;        /* direction counters */
  265.     int fx = md->mx, fy = md->my;    /* current location */
  266.     int nfx = fx, nfy = fy,        /* new location */ 
  267.     d1, d2;                /* shortest distances */
  268.  
  269.     /*
  270.      * It is possible that the monster at (fx,fy) is not the md when:
  271.      * the md rushed the hero and failed, and is now starting back.
  272.      */
  273.     if (m_at(fx, fy) == md) {
  274.     remove_monster(fx, fy);        /* pick up from orig position */
  275.     newsym(fx, fy);
  276.     }
  277.  
  278.     /*
  279.      * At the beginning and exit of this loop, md is not placed in the
  280.      * dungeon.
  281.      */
  282.     while (1) {
  283.     /* Find a good location next to (fx,fy) closest to (tx,ty). */
  284.     d1 = dist2(fx,fy,tx,ty);
  285.     for (dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++)
  286.         if ((dx || dy) && isok(fx+dx,fy+dy) && 
  287.                        !IS_STWALL(levl[fx+dx][fy+dy].typ)) {
  288.         d2 = dist2(fx+dx,fy+dy,tx,ty);
  289.         if (d2 < d1) {
  290.             d1 = d2;
  291.             nfx = fx+dx;
  292.             nfy = fy+dy;
  293.         }
  294.         }
  295.  
  296.     /* Break if the md couldn't find a new position. */
  297.     if (nfx == fx && nfy == fy) break;
  298.  
  299.     fx = nfx;            /* this is our new position */
  300.     fy = nfy;
  301.  
  302.     /* Break if the md reaches its destination. */
  303.     if (fx == tx && fy == ty) break;
  304.  
  305.     if ((mon = m_at(fx,fy)) != 0)    /* save monster at this position */
  306.         verbalize(md_exclamations());
  307.     else if (fx == u.ux && fy == u.uy)
  308.         verbalize("Excuse me.");
  309.  
  310.     place_monster(md,fx,fy);    /* put md down */
  311.     newsym(fx,fy);            /* see it */
  312.     flush_screen(0);        /* make sure md shows up */
  313.     delay_output();            /* wait a little bit */
  314.  
  315.     /* Remove md from the dungeon.  Restore original mon, if necessary. */
  316.     if (mon) {
  317.         if ((mon->mx != fx) || (mon->my != fy))
  318.         place_worm_seg(mon, fx, fy);
  319.         else
  320.         place_monster(mon, fx, fy);
  321.     } else
  322.         remove_monster(fx, fy);
  323.     newsym(fx,fy);
  324.     }
  325.  
  326.     /*
  327.      * Check for a monster at our stopping position (this is possible, but
  328.      * very unlikely).  If one exists, then have the md leave in disgust.
  329.      */
  330.     if ((mon = m_at(fx, fy)) != 0) {
  331.     place_monster(md, fx, fy);    /* display md with text below */
  332.     newsym(fx, fy);
  333.     verbalize("This place's too crowded.  I'm outta here.");
  334.  
  335.     if ((mon->mx != fx) || (mon->my != fy))    /* put mon back */
  336.         place_worm_seg(mon, fx, fy);
  337.     else
  338.         place_monster(mon, fx, fy);
  339.  
  340.     newsym(fx, fy);
  341.     return FALSE;
  342.     }
  343.  
  344.     place_monster(md, fx, fy);    /* place at final spot */
  345.     newsym(fx, fy);
  346.     flush_screen(0);
  347.     delay_output();            /* wait a little bit */
  348.  
  349.     return TRUE;
  350. }
  351.  
  352. /* Deliver a scroll of mail. */
  353. /*ARGSUSED*/
  354. static void
  355. newmail(info)
  356. struct mail_info *info;
  357. {
  358.     struct monst *md;
  359.     coord start, stop;
  360.     boolean message_seen = FALSE;
  361.  
  362.     /* Try to find good starting and stopping places. */
  363.     if (!md_start(&start) || !md_stop(&stop,&start)) goto give_up;
  364.  
  365.     /* Make the daemon.  Have it rush towards the hero. */
  366.     if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y))) goto give_up;
  367.     if (!md_rush(md, stop.x, stop.y)) goto go_back;
  368.  
  369.     message_seen = TRUE;
  370. # ifdef NO_MAILREADER
  371.     if (info->message_typ) {
  372.     verbalize("Hello, %s!  You have some mail in the outside world.", plname);
  373.     goto go_back;
  374.     }    
  375. # endif /* NO_MAILREADER */
  376.  
  377.     verbalize("Hello, %s!  %s.", plname, info->display_txt);
  378.  
  379. # ifndef NO_MAILREADER
  380.     if (info->message_typ) {
  381.     struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE);
  382.     if (distu(md->mx,md->my) > 2)
  383.         verbalize("Catch!");
  384.     display_nhwindow(WIN_MESSAGE, FALSE);
  385.     if (info->object_nam) {
  386.         obj = oname(obj, info->object_nam, FALSE);
  387.         if (info->response_cmd) {    /*(hide extension of the obj name)*/
  388.         int namelth = info->response_cmd - info->object_nam - 1;
  389.         if ( namelth <= 0 || namelth >= (int) obj->onamelth )
  390.             impossible("mail delivery screwed up");
  391.         else
  392.             *(ONAME(obj) + namelth) = '\0';
  393.         /* Note: renaming object will discard the hidden command. */
  394.         }
  395.     }
  396.     obj = hold_another_object(obj, "Oops!",
  397.                   (const char *)0, (const char *)0);
  398.     }
  399. # endif /* NO_MAILREADER */
  400.  
  401.     /* zip back to starting location */
  402. go_back:
  403.     (void) md_rush(md, start.x, start.y);
  404.     mongone(md);
  405.     /* deliver some classes of messages even if no daemon ever shows up */
  406. give_up:
  407.     if (!message_seen && info->message_typ == MSG_OTHER)
  408.     pline("Hark!  \"%s.\"", info->display_txt);
  409. }
  410.  
  411. #endif /* OVLB */
  412.  
  413. # if !defined(UNIX) && !defined(VMS)
  414.  
  415. #ifdef OVL0
  416.  
  417. void
  418. ckmailstatus()
  419. {
  420.     if (u.uswallow) return;
  421.     if (mustgetmail < 0) {
  422. #ifdef AMIGA
  423.         mustgetmail=(moves<2000)?(100+rn2(2000)):(2000+rn2(3000));
  424. #endif
  425.         return;
  426.     }
  427.     if (--mustgetmail <= 0) {
  428.         static struct mail_info
  429.             deliver = {MSG_MAIL,"I have some mail for you",0,0};
  430.         newmail(&deliver);
  431.         mustgetmail = -1;
  432.     }
  433. }
  434.  
  435. #endif /* OVL0 */
  436. #ifdef OVLB
  437.  
  438. /*ARGSUSED*/
  439. void
  440. readmail(otmp)
  441. struct obj *otmp;
  442. {
  443.     char *junk[]={
  444.     "Please disregard previous letter.",
  445.     "Welcome to NetHack 3.1!",
  446. #ifdef AMIGA
  447.     "Only Amiga makes it possible.",
  448.     "CATS have all the answers.",
  449. #endif
  450.     "Report bugs to nethack-bugs@linc.cis.upenn.edu"
  451.     };
  452.  
  453.     pline("It reads:  \"%s\"", junk[rn2(SIZE(junk))]);
  454. }
  455.  
  456. #endif /* OVLB */
  457.  
  458. # endif /* !UNIX && !VMS */
  459.  
  460. # ifdef UNIX
  461.  
  462. #ifdef OVL0
  463.  
  464. void
  465. ckmailstatus()
  466. {
  467.     if(!mailbox || u.uswallow
  468. #  ifdef MAILCKFREQ
  469.             || moves < laststattime + MAILCKFREQ
  470. #  endif
  471.                             )
  472.         return;
  473.  
  474.     laststattime = moves;
  475.     if(stat(mailbox, &nmstat)){
  476. #  ifdef PERMANENT_MAILBOX
  477.         pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox);
  478.         mailbox = 0;
  479. #  else
  480.         nmstat.st_mtime = 0;
  481. #  endif
  482.     } else if(nmstat.st_mtime > omstat.st_mtime) {
  483.         if(nmstat.st_size) {
  484.             static struct mail_info
  485.                 deliver = {MSG_MAIL,"I have some mail for you",0,0};
  486.             newmail(&deliver);
  487.         }
  488.         getmailstatus();    /* might be too late ... */
  489.     }
  490. }
  491.  
  492. #endif /* OVL0 */
  493.  
  494. #ifdef OVLB
  495.  
  496. /*ARGSUSED*/
  497. void
  498. readmail(otmp)
  499. struct obj *otmp;
  500. {
  501. #  ifdef DEF_MAILREADER            /* This implies that UNIX is defined */
  502.     register const char *mr = 0;
  503.  
  504.     display_nhwindow(WIN_MESSAGE, FALSE);
  505.     if(!(mr = getenv("MAILREADER")))
  506.         mr = DEF_MAILREADER;
  507.  
  508.     if(child(1)){
  509.         (void) execl(mr, mr, NULL);
  510.         terminate(1);
  511.     }
  512. #  else
  513. #   ifndef AMS              /* AMS mailboxes are directories */
  514.     display_file(mailbox, TRUE);
  515. #   endif /* AMS */
  516. #  endif /* DEF_MAILREADER */
  517.  
  518.     /* get new stat; not entirely correct: there is a small time
  519.        window where we do not see new mail */
  520.     getmailstatus();
  521. }
  522.  
  523. #endif /* OVLB */
  524.  
  525. # endif /* UNIX */
  526.  
  527. # ifdef VMS
  528.  
  529. #ifdef OVL0
  530.  
  531. volatile int broadcasts = 0;
  532.  
  533. void
  534. ckmailstatus()
  535. {
  536.     struct mail_info *brdcst, *parse_next_broadcast();
  537.  
  538.     if(u.uswallow) return;
  539.  
  540.     while (broadcasts > 0) {    /* process all trapped broadcasts [until] */
  541.     broadcasts--;
  542.     if ((brdcst = parse_next_broadcast()) != 0) {
  543.         newmail(brdcst);
  544.         break;        /* only handle one real message at a time */
  545.     }
  546.     }
  547. }
  548.  
  549. #endif /* OVL0 */
  550.  
  551. #ifdef OVLB
  552.  
  553. void
  554. readmail(otmp)
  555. struct obj *otmp;
  556. {
  557. #  ifdef SHELL    /* can't access mail reader without spawning subprocess */
  558.     const char *txt, *cmd;
  559.     char *p, buf[BUFSZ], qbuf[BUFSZ];
  560.     int len;
  561.  
  562.     /* there should be a command hidden beyond the object name */
  563.     txt = otmp->onamelth ? ONAME(otmp) : "";
  564.     len = strlen(txt);
  565.     cmd = (len + 1 < otmp->onamelth) ? txt + len + 1 : (char *) 0;
  566.     if (!cmd || !*cmd) cmd = "SPAWN";
  567.  
  568.     Sprintf(qbuf, "System command (%s)", cmd);
  569.     getlin(qbuf, buf);
  570.     if (*buf != '\033') {
  571.     for (p = eos(buf); p > buf; *p = '\0')
  572.         if (*--p != ' ') break;    /* strip trailing spaces */
  573.     if (*buf) cmd = buf;        /* use user entered command */
  574.     if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!"))
  575.         cmd = (char *) 0;        /* interactive escape */
  576.  
  577.     vms_doshell(cmd, TRUE);
  578.     (void) sleep(1);
  579.     }
  580. #  endif /* SHELL */
  581. }
  582.  
  583. #endif /* OVLB */
  584.  
  585. # endif /* VMS */
  586.  
  587. #endif /* MAIL */
  588.  
  589. /*mail.c*/
  590.